package sdedit

import java.util.ArrayList
import java.util.List
import org.eclipse.emf.ecore.EObject
import org.eclipse.emf.ecore.resource.Resource
import org.eclipse.xtext.generator.IFileSystemAccess
import sdmakerz_v1.coresyntax.SDMakerV1RuntimeModule
import sdmakerz_v1.coresyntax.SDMakerV1Utils
import sdmakerz_v1.coresyntax.generator.ContextImpl
import sdmakerz_v1.coresyntax.generator.DiagramRefDataImpl
import sdmakerz_v1.coresyntax.generator.interfaces.IConfigData
import sdmakerz_v1.coresyntax.generator.interfaces.IContext
import sdmakerz_v1.coresyntax.generator.interfaces.IDiagramRefData
import sdmakerz_v1.coresyntax.generator.interfaces.ISDMakerModelLevelGen
import sdmakerz_v1.coresyntax.sDMakerV1.Actor
import sdmakerz_v1.coresyntax.sDMakerV1.Alt
import sdmakerz_v1.coresyntax.sDMakerV1.Binding
import sdmakerz_v1.coresyntax.sDMakerV1.Call
import sdmakerz_v1.coresyntax.sDMakerV1.Config
import sdmakerz_v1.coresyntax.sDMakerV1.Diagram
import sdmakerz_v1.coresyntax.sDMakerV1.DiagramElement
import sdmakerz_v1.coresyntax.sDMakerV1.DiagramRef
import sdmakerz_v1.coresyntax.sDMakerV1.Eval
import sdmakerz_v1.coresyntax.sDMakerV1.Import
import sdmakerz_v1.coresyntax.sDMakerV1.Loop
import sdmakerz_v1.coresyntax.sDMakerV1.Obj
import sdmakerz_v1.coresyntax.sDMakerV1.Opt
import sdmakerz_v1.coresyntax.sDMakerV1.SDMakerModel
import sdmakerz_v1.coresyntax.generator.interfaces.IInputData

/** 
 * La classe principale de generation vers SDEDIT
 *
 * @author pstl-team
 */
class SDMakerToSDEditGen implements ISDMakerModelLevelGen {
    
    /* On conserve une liste des noms d'acteurs et objets deja declare
     * pour eviter des declarations redondantes.*/
    List<String> alreadyDeclared;
    
    /* indique utiliser pour eviter la generation de beaucoup de ligne blanche
     * lors de la generation de diagramme (voir la methode correspondante)
     */
    boolean whiteLineSet;
	
	
	
	override boolean canHandle(Resource input) {
		var boolean canHandle= false;

		for(EObject eo : input.
							allContents.
							toIterable){
			if(eo instanceof SDMakerModel){
				canHandle= true;
			}
		}	

		return canHandle
	}
        
	override doGenerate(IContext context, IFileSystemAccess fsa) {
		var IInputData inputData= context.getInputData;
		var Resource input= inputData.getInput;
		
	    alreadyDeclared= new ArrayList<String>();
	    whiteLineSet= false;
	    
	    for(d: input.allContents.toIterable.filter(typeof(Diagram))) { 
            var ContextImpl c= new ContextImpl();
            var IConfigData config= 
            	SDMakerV1RuntimeModule::
            		bindIConfigData.newInstance
            var IDiagramRefData refData= 
            	SDMakerV1RuntimeModule::
            		bindIDiagramRefData.newInstance
            
            
            config.init(null);
            refData.init(d, null, null, d.name);
            c.setInput(inputData);
            c.setConfigData(config);
            c.setDiagramRefData(refData)
            
            fsa.generateFile("sdedit-gen/"+
            	             inputData.getOutputBasedName+"_"+
            	             d.name + ".sd", 
            	             compileSDEditDiagram(d, c)
            )
        }
	}

    def CharSequence compileSDEditDiagram(Diagram d, ContextImpl c) {
        var CharSequence charSequenceResult=''''''
        /* On a besoin de compiler les Actor et les Obj de manieres
         * separe pour pouvoir inserer une ligne blanche de separation
         * necessaire a SDEdit. */
        
        charSequenceResult=
        '''
        «FOR elt: d.diagram_elts.filter(typeof(Actor))»
            «compileActor(elt, c)»
        «ENDFOR»
        «FOR elt: d.diagram_elts.filter(typeof(Obj))»
            «compileObj(elt, c)»
        «ENDFOR»
        '''
        
        if(whiteLineSet==false){
           whiteLineSet= true;
           charSequenceResult= charSequenceResult +
           '''
           
           
           ''' 
        }
        
        charSequenceResult= charSequenceResult +
        '''
        «FOR elt: d.diagram_elts»
            «compile(elt, c)»
        «ENDFOR»
        '''     
    }
    
    def CharSequence compile(DiagramElement elt, ContextImpl c) {
        switch elt {
            Call :
                compileCall(elt, c)
            Alt :
                compileAlt(elt, c)
            Loop :
                compileLoop(elt, c)
            Opt :
                compileOpt(elt, c)
            DiagramRef :
                compileDiagramRef(elt, c)
            Actor :
                /* Seul un acteur de diagramme refere peut matcher.
                 * Celui-ci n'est pas a compiler */
                ''''''
            Obj :
                /* Seul un objet de diagramme refere peut matcher.
                 * Celui-ci n'est pas a compiler */
                ''''''
            Import:
                ''''''
            Config:
                ''''''
            Eval:
                ''''''
            Binding:
                ''''''
            default :
                '''not_supported'''
        }
    }
    
    def CharSequence compileOpt(Opt opt, ContextImpl c) { 
        '''
        [c:opt]
            «FOR elt: opt.opts»
                «compile(elt, c)»
            «ENDFOR»            
        [/c]
        '''
    }

    def CharSequence compileLoop(Loop loop, ContextImpl c) { 
        '''
        [c:loop]
            «FOR elt: loop.body»
                «compile(elt, c)»
            «ENDFOR»            
        [/c]
        '''
    }

    def CharSequence compileAlt(Alt alt, ContextImpl c) { 
        '''
        [c:alt]
            «FOR elt: alt.conseq.conseq_elts»
                «compile(elt, c)»
            «ENDFOR»
            «FOR alter: alt.alters»
        --[else]
            «FOR elt: alter.alter_elts»
                «compile(elt, c)»
            «ENDFOR»
            «ENDFOR»        
        [/c]
        '''
    }

    def CharSequence compileCall(Call call, ContextImpl c) { 
        var String callerName= c.diagramRefData.getNewName(call.caller);
        var String calledName= c.diagramRefData.getNewName(call.called);
        
        '''
        «callerName»:«calledName».«call.name»()
        «IF call.nested_calls != null»
            «FOR e: call.nested_calls»
                «compile(e, c)»
            «ENDFOR»
        «ENDIF»
        '''
    }


    def CharSequence compileActor(Actor actor, ContextImpl c) {
        var CharSequence charSequenceResult='''''';
        var String actorName= c.diagramRefData.getNewName(actor.name);
        if(SDMakerV1Utils::contains(alreadyDeclared, actorName)==false){  
            alreadyDeclared.add(actorName)
            charSequenceResult=
            '''
                «actorName»:Actor
            '''
        } 
        
        return charSequenceResult
    }	
    
    
    def CharSequence compileObj(Obj obj, ContextImpl c) { 
        var CharSequence charSequenceResult='''''';
        var String objName= c.diagramRefData.getNewName(obj.name)
        
        if(SDMakerV1Utils::contains(alreadyDeclared, objName)==false){  
            alreadyDeclared.add(objName)
            charSequenceResult=
            '''
                «objName»: «obj.type.identifier»
            '''
        }
        
        return charSequenceResult; 
    }
    
    def CharSequence compileDiagramRef(DiagramRef diagramRef, ContextImpl c) { 
        var CharSequence charSequenceResult='''''';
        var oldRefData= c.diagramRefData;
        var DiagramRefDataImpl newRefData= new DiagramRefDataImpl();
//        var Resource oldInput= c.currentInput;
//        var Resource input= c.currentInput;
        var Diagram d= null;
        
        /* On altere le contexte de generation */
        
//        if(diagramRef.uriName!=null){
//            input= c.getResourceByUriName(diagramRef.qualifiedName, 
//            	                          diagramRef.name);
//            );
//        }
//        if(input != oldInput){
//            c.setInput(input);
//        }
        
        
        d= c.findDiagram(diagramRef.ref);
        newRefData.init(d, diagramRef, c.diagramRefData, d.name);
        c.setDiagramRefData(newRefData);
            
        /* On compile dans le nouveau contexte */    
        
        charSequenceResult= compileSDEditDiagram(d, c);
        
//        /* On restaure le contexte */
//        
        c.setDiagramRefData(oldRefData);
//        c.setInput(oldInput);
    
        return charSequenceResult;
    }
}